/*==========================================================================
 * 
 * HexTableItem.java
 * 
 * $Author: randallco $
 * $Revision: 1.5 $
 * $Date: 2006/03/20 01:16:54 $
 * $Name:  $
 * 
 * Created on 24-Jan-2004
 * Created by Marcel Palko alias Randallco (randallco@users.sourceforge.net)
 *==========================================================================*/
package net.sourceforge.ehep.gui;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.PreferenceConverter;

import net.sourceforge.ehep.EhepPlugin;
import net.sourceforge.ehep.core.*;

/**
 * @author Marcel Palko
 * @author randallco@users.sourceforge.net
 */
public class HexTableItem extends Item implements Cloneable {
	private TableItem tableItem;
	private HexTable parent;
	
	public HexTableItem(HexTable parent, int style) {
		super(parent, style);
		this.parent = parent;
        parent.addItem(this);
		tableItem = new TableItem(parent.getTable(), style);
		tableItem.setData(this);
	}

	public HexTableItem(HexTable parent, int style, int index) {
		super(parent, style, index);
		this.parent = parent;
        parent.addItem(this, index);
		tableItem = new TableItem(parent.getTable(), style, index);
		tableItem.setData(this);
	}

	public HexTableItem getItem(TableItem tableItem) {
		if (tableItem == null) return null;
		if (this.tableItem == tableItem) return this;
		return null;
	}

    /**
     * Returns the zero-based index of the row
     * @return Zero-based index of the row or -1 if parent is null
     */
    public int getIndex() {
        return (parent != null) ? parent.getRowIndex(this) : -1;
    }
    /**
	 * Gets the Display.
	 * <p>
	 * This method gets the Display that is associated
	 * with the widget.
	 *
	 * @return the widget data
	 *
	 * @exception SWTError <ul>
	 *          <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
	 *          <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
	 * </ul>
	 * </p>
	 */
	public Display getDisplay() {
		HexTable parent = this.parent;
		if (parent == null) throw new SWTError(SWT.ERROR_WIDGET_DISPOSED);
		return parent.getDisplay();
	}

	/**
	 * Gets the widget bounds at the specified index.
	 * <p>
	 * @return the widget bounds at the specified index
	 *
	 * @exception SWTError <ul>
	 *          <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
	 *          <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 
	 *    </ul>
	 */
	public Rectangle getBounds(int index) {
		if (tableItem != null) {
			return tableItem.getBounds(index);
		} else {
			return new Rectangle(0, 0, 0, 0);
		}
	}

	public TableItem getItem() {
		return tableItem;
	}
	
	/**
	 * Returns the item data in a String buffer from certain position to the end
	 * @param columnIndex in the range from 0 to 15
	 * @return String buffer with item data or null if columnIndex is out-of-range
	 */
	public String[] getItemData(int columnIndex) {
		if (columnIndex >= EHEP.TABLE_NUM_DATA_COLUMNS) return null;
		int itemSize = getItemSize() - columnIndex;
		if (itemSize <= 0) return null;
		String[] buffer = new String[itemSize];
		for (int i = 0; i < itemSize; i++) {
			buffer[i] = tableItem.getText(columnIndex + i + 1);
		} // for
		return buffer;
	}

	public HexTable getParent() {
		return parent;
	}
	
	public void setText(int index, String string) {
		tableItem.setText(index, string);
	}

	public String getText(int index) {
		if (index < 0 || index >= EHEP.TABLE_NUM_COLUMNS) return "";
		return tableItem.getText(index);
	}

	/**
	 * Deletes data from the specified column position to the end of this table item
	 * @param columnIndexBegin
	 */
	public void delete(int columnIndexBegin) {
		delete(columnIndexBegin, EHEP.TABLE_NUM_DATA_COLUMNS-1);
	}

	/**
	 * Deletes data in specified range within this table item
	 * @param columnIndexBegin
	 * @param columnIndexEnd
	 */
	public void delete(int columnIndexBegin, int columnIndexEnd) {
		for (int i = columnIndexBegin; i <= columnIndexEnd; i++) {
			tableItem.setText(i+1, EHEP.TABLE_EMPTY_CELL);
			//
			// Set table row color
			//
			tableItem.setBackground(i+1, EhepPlugin.getColor(PreferenceConverter.getColor(EhepPlugin.getDefault().getPreferenceStore(), EHEP.PROPERTY_COLOR_BACKGROUND_TABLE)));
			tableItem.setForeground(i+1, EhepPlugin.getColor(PreferenceConverter.getColor(EhepPlugin.getDefault().getPreferenceStore(), EHEP.PROPERTY_COLOR_FOREGROUND_TABLE)));
		} // for
	}

	public void setBackground(Color color) {
		tableItem.setBackground(color); 
	}

	public void setBackground(int index, Color color) {
		tableItem.setBackground(index, color);
	}

	public Color getBackground(int index) {
		return tableItem.getBackground(index);
	}

	public String getBackgroundRGBString(int index) {
		Color c = tableItem.getBackground(index);
		return "" + c.getRed() + "," + c.getGreen() + "," + c.getBlue();
	}

	public void setForeground(Color color) {
		tableItem.setForeground(color); 
	}

	public void setForeground(int index, Color color) {
		tableItem.setForeground(index, color); 
	}

	public Color getForeground(int index) {
		return tableItem.getForeground(index);
	}

	public String getForegroundRGBString(int index) {
		Color c = tableItem.getForeground(index);
		return "" + c.getRed() + "," + c.getGreen() + "," + c.getBlue();
	}

	public String getCellStatus(int index, Color[][] currentColorSettings) {
		Color bc = tableItem.getBackground(index);
		Color fc = tableItem.getForeground(index);
		
		for (int i = 0; i < currentColorSettings.length; i++) {
			if (bc.equals(currentColorSettings[i][0]) && fc.equals(currentColorSettings[i][1])) {
				return (i == 0) ? "" : "," + i;
			} // if
		} // for
		
		return "";
	}

	public void dispose() {
		super.dispose();

		if (!parent.isInDispose()) {
			if (tableItem != null) tableItem.dispose();
		} // if

		tableItem = null;
	}

	/**
	 * Fills empty item cells with 'filler'
	 */
	public void itemFiller() {
		for (int i = 0; i < EHEP.TABLE_NUM_DATA_COLUMNS; i++) {
			if (tableItem.getText(i+1).length() == 0) {
				tableItem.setText(i+1, EHEP.TABLE_EMPTY_CELL);
				//
				// Set table row color
				//
				tableItem.setBackground(i+1, EhepPlugin.getColor(PreferenceConverter.getColor(EhepPlugin.getDefault().getPreferenceStore(), EHEP.PROPERTY_COLOR_BACKGROUND_TABLE)));
				tableItem.setForeground(i+1, EhepPlugin.getColor(PreferenceConverter.getColor(EhepPlugin.getDefault().getPreferenceStore(), EHEP.PROPERTY_COLOR_FOREGROUND_TABLE)));
			} // if
		} // for
	}

	/**
	 * Calculates the size of the table item (number of non-empty cells)
	 * @return number of non-empty cells in the table row
	 */
	public int getItemSize() {
		int numDataCells = 0;
		String data; 
		
		for (int i = 0; i < EHEP.TABLE_NUM_DATA_COLUMNS; i++) {
			data = tableItem.getText(i+1);
			if (!data.equals(EHEP.TABLE_EMPTY_CELL) && data.length() > 0) {
				numDataCells++;
			} // for
		} // for
		
		return numDataCells;
	}

	/**
	 * Adds data to the item from the buffer
	 * @param buffer with data
	 * @param dataAvailable amount of data available in the buffer
	 * @param dataStartIndex "pointer" to the buffer where data starts
	 * @return number of bytes "consumed" by the function call or -1 in case of missing data or buffer
	 */
	public int addData(int bufferIndex, int rowIndex, byte[] buffer, int dataAvailable, int dataStartIndex) {
		int dataCounter = 0;
		
		if (dataStartIndex >= dataAvailable) {
			//
			// Out-of-buffer-range
			//
			return -1;
		}
		
		//
		// Get row size
		//
		int currentItemSize = getItemSize();
		
		if (currentItemSize == EHEP.TABLE_NUM_DATA_COLUMNS) {
			//
			// The row is already full - no data can be added
			//
			return 0;
		} // if
		
		//
		// Define the offset column value
		//
		tableItem.setText(0, Utils.int2string(bufferIndex*EHEP.BUFFER_SIZE + 16*rowIndex) + ":");
		
		for (int i = currentItemSize; i < EHEP.TABLE_NUM_DATA_COLUMNS && (dataStartIndex + dataCounter) < dataAvailable; i++) {
			//
			// Read buffer, convert bytes to strings and store them into the row
			//
			tableItem.setText(i+1, Utils.byte2string(buffer[dataStartIndex + dataCounter]));
			dataCounter++;
		} // for

		//
		// Fill the item's empty cells with a 'filler'
		//
		itemFiller();		

		//
		// Generate the character view column for this row
		//
		refreshCharacterView();

		return dataCounter;
	}

	public boolean refreshCharacterView() {
		return refreshCharacterView(parent.getHexEditorControl().getCurrentEncoding());
	}

	public boolean refreshCharacterView(String encoding) {
		int rowLength = EHEP.TABLE_NUM_DATA_COLUMNS;
		byte[] hexData = new byte[1];
		String str;
		StringBuffer stringData = new StringBuffer(EHEP.TABLE_NUM_DATA_COLUMNS);

		try {
			//
			// Loop for each column in the row
			//
			for (int j = 0; j < rowLength; j++) {
				str = tableItem.getText(j+1);

				if (str.equals(EHEP.TABLE_EMPTY_CELL)) {
					//
					// We reached end-of-data, stop looping and add spare
					//
					stringData.append("                ".substring(j));
					break;
				}

				//
				// Convert hex string into the byte
				//
				hexData[0] = Utils.string2byte(str);
				str = new String(hexData, encoding);
				stringData.append(((Character.isWhitespace(str.charAt(0)) && !Character.isSpaceChar(str.charAt(0))) || Character.isISOControl(str.charAt(0))) ? "." : str);
				str = null;
			} // for

			//
			// Put string into the table
			//
			tableItem.setText(EHEP.TABLE_NUM_COLUMNS-1, stringData.toString());
			stringData = null;
		} // try

		catch (java.io.UnsupportedEncodingException e) {
			//
			// Unsupported encoding or other exception
			//
			MessageDialog.openWarning(parent.getShell(), EHEP.DIALOG_TITLE_ERROR, "Exception in displayCharacterView(), encoding = " + encoding + ":\n" + e.toString());
			return false;
		} // catch

		return true;
	}
	
	/**
	 * overwrite data to the item from the buffer
	 * @param buffer with data
	 * @param dataAvailable amount of data available in the buffer
	 * @param dataStartIndex "pointer" to the buffer where data starts
	 * @return number of bytes "consumed" by the function call or -1 in case of missing data or buffer
	 * @author Sophia Ghins
	 */
	public int overwriteData(int bufferIndex, int rowIndex, byte[] buffer, int dataAvailable, int dataStartIndex) {
		int dataCounter = 0;
		int currentItemSize = 0;
		
		// Out-of-buffer-range
		if (dataStartIndex >= dataAvailable) {
			return -1;
		}
		
		// Define the offset column value
		tableItem.setText(0, Utils.int2string(bufferIndex*EHEP.BUFFER_SIZE + 16*rowIndex) + ":");
		
		// Read buffer, convert bytes to strings and store them into the row
		for (int i = currentItemSize; i < EHEP.TABLE_NUM_DATA_COLUMNS && (dataStartIndex + dataCounter) < dataAvailable; i++) {
			tableItem.setText(i+1, Utils.byte2string(buffer[dataStartIndex + dataCounter]));
			dataCounter++;
		} // for

		// Fill the item's empty cells with a 'filler'
		//itemFiller();		

		// Generate the character view column for this row
		refreshCharacterView();

		return dataCounter;
	}
	
	
}
